/**
* ORcycle, Copyright 2014, 2015, PSU Transportation, Technology, and People Lab.
*
* @author Robin Murray <robin5@pdx.edu> (code)
* @author Miguel Figliozzi <figliozzi@pdx.edu> and ORcycle team (general app
* design and features, report questionnaires and new ORcycle features)
*
* For more information on the project, go to
* http://www.pdx.edu/transportation-lab/orcycle and http://www.pdx.edu/transportation-lab/app-development
*
* Updated/modified for Oregon pilot study and app deployment.
*
* ORcycle is free software: you can redistribute it and/or modify it under the
* terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or any later version.
* ORcycle is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with
* ORcycle. If not, see <http://www.gnu.org/licenses/>.
*
*/
package edu.pdx.cecs.orcycle;
import java.text.SimpleDateFormat;
import java.util.Locale;
import java.util.TimeZone;
import java.util.Timer;
import java.util.TimerTask;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import android.os.Handler;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient.ConnectionCallbacks;
import com.google.android.gms.common.GooglePlayServicesClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.UiSettings;
import com.google.android.gms.maps.model.LatLng;
/**
* @author Robin
*
*/
public class FragmentMainInput extends Fragment
implements ConnectionCallbacks, OnConnectionFailedListener, LocationListener, IRecordServiceListener,
OnMyLocationButtonClickListener {
private static final String MODULE_TAG = "FragmentMainInput";
private static final float CF_METERS_TO_MILES = 0.00062137f; // Conversion factor meters to miles
private static final float CF_MILES_TO_LBS_CO2 = 0.93f; // Conversion factor miles to pounds CO2 saved
private static final float CF1_MILES_TO_KCALORIES = 49.0f; // Conversion factor part 1 miles to Kcalories burned
private static final float CF2_MILES_TO_KCALORIES = -1.69f; // Conversion factor part 2 miles to Kcalories burned
private static final float METERS_PER_SECOND_TO_MILES_PER_HOUR = 2.2369f;
private static final int FMI_USER_PAUSED = 1;
private static final int FMI_NOTE_PAUSED = 2;
private static final int FMI_FINISH_PAUSED = 3;
public enum Result {UNDEFINED, SAVE_TRIP, REPORT, NO_GPS, GET_USER_INFO, SHOW_INSTRUCTIONS,
SHOW_WELCOME, SHOW_DIALOG_USER_INFO, SHOW_TUTORIAL };
private Result result;
// Reference to Global application object
private MyApplication myApp = null;
private Controller controller = null;
// Reference to recording service;
private IRecordService recordingService;
// UI Elements
private Button buttonStart = null;
private Button buttonPause = null;
private Button buttonResume = null;
private Button buttonFinish = null;
private Button buttonNote = null;
private TextView txtDuration = null;
private TextView txtDistance = null;
private TextView txtAvgSpeed = null;
private TextView txtCO2 = null;
private TextView txtCalories = null;
private Timer statusUpdateTimer;
final Handler serviceConnectionHandler = new Handler();
private Timer serviceConnectionTimer;
final Handler taskHandler = new Handler();
private Location currentLocation = null;
private boolean backFromInstructions;
// Format used to show elapsed time to user when recording trips
private final SimpleDateFormat tripDurationFormat = new SimpleDateFormat("HH:mm:ss", Locale.US);
// *********************************************************************************
// *
// *********************************************************************************
private GoogleMap map;
private LocationClient mLocationClient;
private static final LocationRequest REQUEST = LocationRequest.create()
.setInterval(5000) // 5 seconds
.setFastestInterval(16) // 16ms = 60fps
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
// *********************************************************************************
// * Constructor
// *********************************************************************************
public FragmentMainInput() {
tripDurationFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
}
// *********************************************************************************
// * Fragment Handlers
// *********************************************************************************
/**
* Handler: onCreateView
* Update distance and speed user interface elements
*/
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.v(MODULE_TAG, "Cycle: onCreateView()");
View rootView = null;
result = Result.UNDEFINED;
try {
// Convenient pointer to global application object
myApp = MyApplication.getInstance();
controller = myApp.getController();
// Folloing line for debugging only
//myApp.setUserProfileUploaded(false);
// Create main user interface window
rootView = inflater.inflate(R.layout.activity_main_input, container, false);
// Initialize map parameters
setUpMapIfNeeded();
// Setup the button to start recording
buttonStart = (Button) rootView.findViewById(R.id.buttonStart);
buttonStart.setVisibility(View.GONE);
buttonStart.setOnClickListener(new ButtonStart_OnClickListener());
// Setup the button to pause recording
buttonPause = (Button) rootView.findViewById(R.id.buttonPause);
buttonPause.setVisibility(View.GONE);
buttonPause.setOnClickListener(new ButtonPause_OnClickListener());
// Setup the button to resume recording
buttonResume = (Button) rootView.findViewById(R.id.buttonResume);
buttonResume.setVisibility(View.GONE);
buttonResume.setOnClickListener(new ButtonResume_OnClickListener());
// Setup the button to finish recording
buttonFinish = (Button) rootView.findViewById(R.id.buttonFinish);
buttonFinish.setVisibility(View.GONE);
buttonFinish.setOnClickListener(new ButtonFinish_OnClickListener());
// Setup the button to add a note to the trip
buttonNote = (Button) rootView.findViewById(R.id.btn_ami_note_this);
buttonNote.setVisibility(View.GONE);
buttonNote.setOnClickListener(new ButtonNote_OnClickListener());
// Copy from Recording Activity
txtDuration = (TextView) rootView.findViewById(R.id.textViewElapsedTime);
txtDistance = (TextView) rootView.findViewById(R.id.textViewDistance);
txtAvgSpeed = (TextView) rootView.findViewById(R.id.textViewAvgSpeed);
txtCO2 = (TextView) rootView.findViewById(R.id.textViewCO2);
txtCalories = (TextView) rootView.findViewById(R.id.textViewCalories);
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
return rootView;
}
/**
* Set buttons according to application state
* @param appState
*/
private void setupButtons() {
switch(recordingService.getState()) {
case RecordingService.STATE_IDLE:
buttonNote.setVisibility(View.VISIBLE);
buttonStart.setVisibility(View.VISIBLE);
buttonPause.setVisibility(View.GONE);
buttonResume.setVisibility(View.GONE);
buttonFinish.setVisibility(View.GONE);
break;
case RecordingService.STATE_RECORDING:
buttonNote.setVisibility(View.VISIBLE);
buttonStart.setVisibility(View.GONE);
buttonPause.setVisibility(View.VISIBLE);
buttonResume.setVisibility(View.GONE);
buttonFinish.setVisibility(View.VISIBLE);
break;
case RecordingService.STATE_PAUSED:
buttonNote.setVisibility(View.VISIBLE);
buttonStart.setVisibility(View.GONE);
buttonPause.setVisibility(View.GONE);
buttonResume.setVisibility(View.VISIBLE);
buttonFinish.setVisibility(View.VISIBLE);
break;
case RecordingService.STATE_FULL:
buttonNote.setVisibility(View.VISIBLE);
buttonStart.setVisibility(View.GONE);
buttonPause.setVisibility(View.GONE);
buttonResume.setVisibility(View.GONE);
buttonFinish.setVisibility(View.GONE);
break;
}
}
/**
* Handler: onResume
* Called when the <code>activity<code/> will start interacting with the user. At this point
* the <code>activity<code/> is at the top of the <code>activity<code/> stack, with user
* input going to it. Always followed by <code>onPause()<code/>.
* @see <code>onPause<code/> class.
*/
@Override
public void onResume() {
super.onResume();
try {
Log.v(MODULE_TAG, "Cycle: onResume()");
setUpMapIfNeeded();
if (map != null) {
UiSettings mUiSettings = map.getUiSettings();
// Keep the UI Settings state in sync with the checkboxes.
mUiSettings.setZoomControlsEnabled(true);
mUiSettings.setCompassEnabled(true);
mUiSettings.setMyLocationButtonEnabled(true);
map.setMyLocationEnabled(true);
mUiSettings.setScrollGesturesEnabled(true);
mUiSettings.setZoomGesturesEnabled(true);
mUiSettings.setTiltGesturesEnabled(true);
mUiSettings.setRotateGesturesEnabled(true);
}
enableLocationClient();
Intent intent;
Bundle bundle;
if (backFromInstructions) {
backFromInstructions = false;
if (myApp.getUserWelcomeEnabled()) {
controller.finish(setResult(Result.SHOW_WELCOME));
return;
}
}
// Check for responses from User dialogs
else if (null != (intent = getActivity().getIntent())) {
if (null != (bundle = intent.getExtras())) {
String src = bundle.getString(TabsConfig.EXTRA_DSA_ACTIVITY);
if (null != src) {
// Respond to the dialog button that was pressed
int dialogId = bundle.getInt(TabsConfig.EXTRA_DSA_DIALOG_ID, -1);
int buttonPressed = bundle.getInt(TabsConfig.EXTRA_DSA_BUTTON_PRESSED, -1);
boolean ischecked = bundle.getBoolean(DsaDialogActivity.EXTRA_IS_CHECKED, false);
// Check for Welcome Dialog
if (Controller.DSA_ID_WELCOME_DIALOG_ID == dialogId) {
if (ischecked) {
myApp.setUserWelcomeEnabled(false);
}
if (Controller.DSA_ID_WELCOME_DIALOG_INSTRUCTIONS == buttonPressed) {
controller.finish(setResult(Result.SHOW_INSTRUCTIONS));
backFromInstructions = false;
return;
}
}
// Check for HowTo dialog
else if (Controller.DSA_ID_HOW_TO_DIALOG_ID == dialogId) {
if (ischecked) {
myApp.setTutorialEnabled(false);
}
else if (controller.setNextHowToScreen()){
controller.finish(setResult(Result.SHOW_TUTORIAL));
return;
}
}
// Check for UserInfo dialog
else if (Controller.DSA_ID_USER_PROFILE_DIALOG_ID == dialogId) {
if (ischecked) {
myApp.setUserProfileUploaded(true);
}
if (Controller.DSA_ID_USER_PROFILE_DIALOG_OK == buttonPressed) {
controller.finish(setResult(Result.GET_USER_INFO));
return;
}
}
else {
// got a bundle, from unknown dialog?
}
}
}
}
// Show next dialog in sequence
if (!myApp.getCheckedForUserWelcome() && myApp.getUserWelcomeEnabled()) {
myApp.setCheckedForUserWelcome(true);
controller.finish(setResult(Result.SHOW_WELCOME));
}
else if (!myApp.getCheckedForTutorial() && myApp.getTutorialEnabled()) {
myApp.setCheckedForTutorial(true);
controller.finish(setResult(Result.SHOW_TUTORIAL));
}
else if (!myApp.getCheckedForUserProfile()
&& !myApp.getUserProfileUploaded()
&& (myApp.getFirstTripCompleted())) {
myApp.setCheckedForUserProfile(true);
controller.finish(setResult(Result.SHOW_DIALOG_USER_INFO));
}
else if (null == recordingService) {
scheduleServiceConnect();
}
else {
syncDisplayToRecordingState();
}
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
switch(requestCode) {
case Controller.DSA_ID_WELCOME_DIALOG_ID:
backFromInstructions = true;
Log.e(MODULE_TAG, "back from ORcycle");
break;
case Controller.DSA_ID_USER_PROFILE_DIALOG_ID:
break;
}
}
/**
* Set up buttons, and enable status updates
*/
private void syncDisplayToRecordingState() {
switch(recordingService.getState()) {
case RecordingService.STATE_IDLE:
setupButtons();
break;
case RecordingService.STATE_RECORDING:
setupButtons();
break;
case RecordingService.STATE_PAUSED:
if (recordingService.pauseId() == FMI_NOTE_PAUSED) {
recordingService.resumeRecording();
}
setupButtons();
break;
case RecordingService.STATE_FULL:
setupButtons();
dialogTripFinish(true);
break;
default:
Log.e(MODULE_TAG, "Undefined recording state encountered");
break;
}
scheduleStatusUpdates(); // every second
}
/**
* Creates and schedules a timer to update the trip duration.
*/
private void scheduleStatusUpdates() {
statusUpdateTimer = new Timer();
statusUpdateTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
try {
tripStatusHandler.post(tripStatusUpdateTask);
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
}, 0, 1000); // every second
}
private void scheduleServiceConnect() {
serviceConnectionTimer = new Timer();
serviceConnectionTimer.schedule(new TimerTask() {
@Override
public void run() {
serviceConnectionHandler.post(doServiceConnection);
}
}, 2000, 2000); // 2 second delay, at 2 second intervals
}
// *********************************************************************************
// *
// *********************************************************************************
/**
* Handler: onPause
*/
@Override
public void onPause() {
super.onPause();
try {
Log.v(MODULE_TAG, "Cycle: onPause()");
if (statusUpdateTimer != null)
statusUpdateTimer.cancel();
if (serviceConnectionTimer != null)
serviceConnectionTimer.cancel();
if (mLocationClient != null) {
mLocationClient.disconnect();
}
if (recordingService != null) {
recordingService.setListener(null);
}
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
/**
* Handler: onDestroyView
*/
@Override
public void onDestroyView() {
super.onDestroyView();
try {
Log.v(MODULE_TAG, "Cycle: onDestroyView()");
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
/**
* setUpMapIfNeeded: Instantiate the map
*/
private void setUpMapIfNeeded() {
// Do a null check to confirm that we have not already instantiated the map.
if (map == null) {
// Try to obtain the map from the SupportMapFragment.
map = ((SupportMapFragment) getActivity()
.getSupportFragmentManager().findFragmentById(R.id.map))
.getMap();
// Check if we were successful in obtaining the map.
if (map != null) {
map.setMyLocationEnabled(true);
map.setOnMyLocationButtonClickListener(this);
moveCameraToOregon();
}
}
}
// *********************************************************************************
// * Trip Status Update Timers and Runnables
// *********************************************************************************
// Need handler for callbacks to the UI thread
final Handler tripStatusHandler = new Handler();
final Runnable tripStatusUpdateTask = new Runnable() {
public void run() {
try {
updateTripStatus();
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
};
/**
* Update the duration label
*/
private void updateTripStatus() {
try {
if (null != recordingService) {
ApplicationStatus appStatus = myApp.getStatus();
boolean isRecording =
((RecordingService.STATE_RECORDING == recordingService.getState()) ||
(RecordingService.STATE_PAUSED == recordingService.getState()));
if (isRecording) {
TripData tripData = appStatus.getTripData();
if (null != tripData) {
txtDuration.setText(tripDurationFormat.format(tripData.getDuration(true)));
this.updateStatus(tripData.getDistance(), tripData.getAvgSpeedMps(true));
}
}
}
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
// *********************************************************************************
// * WaitForServiceConnection Tasking
// *********************************************************************************
/**
* Class: doServiceConnection
* This task is to be executed after onResume has occurred to assure we still
* have a reference to the recording service. This would happen if the OS
* kicked the service out of memory while the owning activity was dormant.
*/
final Runnable doServiceConnection = new Runnable() {
public void run() {
try {
if (null == recordingService) {
Log.v(MODULE_TAG, "doServiceConnection(): Service not yet connected.");
// See if a service connection has been established
if (null != (recordingService = myApp.getRecordingService())) {
Log.v(MODULE_TAG, "doServiceConnection(): got connection!");
// We now have connection to the service so cancel the timer
serviceConnectionTimer.cancel();
recordingService.setListener(FragmentMainInput.this);
//scheduleTask(TASK_SERVICE_CONNECT_COMPLETE);
syncDisplayToRecordingState();
}
}
else {
Log.v(MODULE_TAG, "doServiceConnection(): Service already connected.");
serviceConnectionTimer.cancel();
syncDisplayToRecordingState();
}
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
} // end of run
};
// *********************************************************************************
// * Button Handlers
// *********************************************************************************
/**
* Class: ButtonStart_OnClickListener
*
* Description: Callback to be invoked when startButton button is clicked
*/
private final class ButtonStart_OnClickListener implements View.OnClickListener {
/**
* Description: Handles onClick for view
*/
public void onClick(View v) {
try {
// Before we go to record, check GPS status
if (!myApp.getStatus().isProviderEnabled()) {
// Alert user GPS not available
dialogNoGps();
}
else {
myApp.startRecording(getActivity());
}
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
finally {
setupButtons();
}
}
}
/**
* Class: ButtonPause_OnClickListener
*
* Description: Callback to be invoked when pauseButton button is clicked
*/
private final class ButtonPause_OnClickListener implements View.OnClickListener {
/**
* Description: Handles onClick for view
*/
public void onClick(View v) {
try {
recordingService.pauseRecording(FMI_USER_PAUSED);
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
finally {
setupButtons();
}
}
}
/**
* Class: ButtonResume_OnClickListener
*
* Description: Callback to be invoked when resumeButton button is clicked
*/
private final class ButtonResume_OnClickListener implements View.OnClickListener {
/**
* Description: Handles onClick for view
*/
public void onClick(View v) {
try {
recordingService.resumeRecording();
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
finally {
setupButtons();
}
}
}
/**
* Class: ButtonFinish_OnClickListener
*
* Description: Callback to be invoked when ButtonFinish button is clicked
*/
private final class ButtonFinish_OnClickListener implements View.OnClickListener {
/**
* Description: Handles onClick for view
*/
public void onClick(View v) {
try {
recordingService.pauseRecording(FMI_FINISH_PAUSED);
setupButtons();
TripData tripData = myApp.getStatus().getTripData();
boolean allowSave = (tripData.getNumPoints() > 0);
dialogTripFinish(allowSave);
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
}
/**
* Class: ButtonNote_OnClickListener
*
* Description: Callback to be invoked when buttonNote button is clicked
*/
private final class ButtonNote_OnClickListener implements View.OnClickListener {
/**
* Description: Handles onClick for view
*/
public void onClick(View v) {
try {
if (null == recordingService) {
Log.e(MODULE_TAG, "Connection to recording service not yet established");
return;
}
long tripId;
switch(recordingService.getState()) {
case RecordingService.STATE_IDLE:
tripId = 0;
break;
case RecordingService.STATE_RECORDING:
recordingService.pauseRecording(FMI_NOTE_PAUSED);
tripId = recordingService.getCurrentTripID();
break;
case RecordingService.STATE_PAUSED:
tripId = recordingService.getCurrentTripID();
break;
case RecordingService.STATE_FULL:
tripId = recordingService.getCurrentTripID();
break;
default:
Log.e(MODULE_TAG, "Invalid recording service state encountered.");
return;
}
NoteData note = NoteData.createNote(getActivity(), tripId);
note.updateNoteStatus(NoteData.STATUS_INCOMPLETE);
controller.finish(setResult(Result.REPORT), tripId, note.getNoteId());
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
}
private FragmentMainInput setResult(Result result) {
this.result = result;
return this;
}
public FragmentMainInput.Result getResult() {
return this.result;
}
// *********************************************************************************
// *
// *********************************************************************************
/**
* Updates the status of a trip being recorded.
* @param distanceMeters Distance travelled in meters
* @param avgSpeedMps Average speed in meters per second
*/
public void updateStatus(float distanceMeters, float avgSpeedMps) {
float distanceMiles = distanceMeters * CF_METERS_TO_MILES;
txtDistance.setText(String.format("%1.1f miles", distanceMiles));
float avgSpeedMph = avgSpeedMps * METERS_PER_SECOND_TO_MILES_PER_HOUR;
txtAvgSpeed.setText(String.format("%1.1f mph", avgSpeedMph));
float calories = distanceMiles * CF1_MILES_TO_KCALORIES + CF2_MILES_TO_KCALORIES;
if (calories < 0.0f) {
calories = 0.0f;
}
txtCalories.setText(String.format("%1.1f kcal", calories));
float lbsCO2 = distanceMiles * CF_MILES_TO_LBS_CO2;
txtCO2.setText(String.format("%1.1f lbs", lbsCO2));
}
/**
* Cancels recording
*/
private void cancelRecording() {
try {
myApp.cancelRecording();
txtDuration = (TextView) getActivity().findViewById(R.id.textViewElapsedTime);
txtDuration.setText(getResources().getString(R.string.fmi_reset_duration));
txtDistance = (TextView) getActivity().findViewById(R.id.textViewDistance);
txtDistance.setText(getResources().getString(R.string.fmi_reset_distance));
txtAvgSpeed = (TextView) getActivity().findViewById(R.id.textViewAvgSpeed);
txtAvgSpeed.setText(getResources().getString(R.string.fmi_reset_avg_speed));
txtCalories = (TextView) getActivity().findViewById(R.id.textViewCalories);
txtCalories.setText(getResources().getString(R.string.fmi_reset_calories));
txtCO2 = (TextView) getActivity().findViewById(R.id.textViewCO2);
txtCO2.setText(getResources().getString(R.string.fmi_reset_co2));
setupButtons();
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
// *********************************************************************************
// * No GPS Dialog
// *********************************************************************************
/**
* Build dialog telling user that the GPS is not available
*/
private void dialogNoGps() {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage(getResources().getString(R.string.fmi_no_gps));
builder.setCancelable(false);
builder.setPositiveButton(getResources().getString(R.string.fmi_no_gps_dialog_ok),
new DialogNoGps_OkListener());
builder.setNegativeButton(getResources().getString(R.string.fmi_no_gps_dialog_cancel),
new DialogNoGps_CancelListener());
final AlertDialog alert = builder.create();
alert.show();
}
private final class DialogNoGps_OkListener implements DialogInterface.OnClickListener {
public void onClick(final DialogInterface dialog, final int id) {
controller.finish(setResult(Result.NO_GPS));
}
}
private final class DialogNoGps_CancelListener implements DialogInterface.OnClickListener {
public void onClick(final DialogInterface dialog, final int id) {
dialog.cancel();
}
}
// *********************************************************************************
// * Trip Finished Dialogs
// *********************************************************************************
/**
* Build dialog telling user to save this trip
*/
private void dialogTripFinish(boolean allowSave) {
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(getResources().getString(R.string.fmi_dtf_save_trip));
if (allowSave) {
builder.setMessage(getResources().getString(R.string.fmi_dtf_query_save));
builder.setNegativeButton(getResources().getString(R.string.fmi_dtf_save),
new DialogTripFinish_OnSaveTripClicked());
}
else {
builder.setMessage(getResources().getString(R.string.fmi_no_gps_data));
}
builder.setNeutralButton(getResources().getString(R.string.fmi_dtf_discard),
new DialogTripFinish_OnDiscardTripClicked());
builder.setPositiveButton(getResources().getString(R.string.fmi_dtf_resume),
new DialogTripFinish_OnContinueTripClicked());
final AlertDialog alert = builder.create();
alert.show();
}
private final class DialogTripFinish_OnSaveTripClicked implements
DialogInterface.OnClickListener {
public void onClick(DialogInterface dialog, int id) {
try {
dialog.cancel();
myApp.finishRecording();
TripData tripData = myApp.getStatus().getTripData();
if (tripData.getNumPoints() > 0) {
controller.finish(setResult(Result.SAVE_TRIP), tripData.tripid);
}
// Otherwise, cancel and go back to main screen
else {
alertUserNoGPSData();
cancelRecording();
}
setupButtons();
}
catch (Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
}
private final class DialogTripFinish_OnDiscardTripClicked implements
DialogInterface.OnClickListener {
public void onClick(DialogInterface dialog, int id) {
try {
dialog.cancel();
cancelRecording();
}
catch (Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
} finally {
setupButtons();
}
}
}
private final class DialogTripFinish_OnContinueTripClicked implements
DialogInterface.OnClickListener {
public void onClick(DialogInterface dialog, int id) {
try {
dialog.cancel();
recordingService.resumeRecording();
} catch (Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
} finally {
setupButtons();
}
}
}
// *********************************************************************************
// * Map Location Tracking
// *********************************************************************************
/**
* Creates a new location client and connects to it
*/
private void enableLocationClient() {
if (mLocationClient == null) {
mLocationClient = new LocationClient(getActivity(), this, this); // OnConnectionFailedListener // ConnectionCallbacks
}
mLocationClient.connect();
}
/**
* Implementation of {@link LocationListener}.
*/
@Override
public void onLocationChanged(Location location) {
try {
currentLocation = new Location(location);
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
private void moveCameraToMyLocation(Location location) {
LatLng myLocation = new LatLng(location.getLatitude(), location.getLongitude());
map.animateCamera(CameraUpdateFactory.newLatLngZoom(myLocation, 16));
}
private void moveCameraToOregon() {
LatLng myLocation = new LatLng(43.8041334 , -120.55420119999996 );
map.animateCamera(CameraUpdateFactory.newLatLngZoom(myLocation, 6));
}
/**
* Callback called when connected to GCore. Implementation of
* {@link ConnectionCallbacks}.
*/
@Override
public void onConnected(Bundle connectionHint) {
try {
mLocationClient.requestLocationUpdates(REQUEST, this); // LocationListener
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
/**
* Callback called when disconnected from GCore. Implementation of
* {@link ConnectionCallbacks}.
*/
@Override
public void onDisconnected() {
try {
// Do nothing
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
/**
* Implementation of {@link OnConnectionFailedListener}.
*/
@Override
public void onConnectionFailed(ConnectionResult result) {
try {
// Do nothing
}
catch(Exception ex) {
Log.e(MODULE_TAG, ex.getMessage());
}
}
@Override
public boolean onMyLocationButtonClick() {
return false;
}
// *********************************************************************************
// * Misc & Helper Functions
// *********************************************************************************
private void alertUserNoGPSData() {
Toast.makeText(getActivity(),
getResources().getString(R.string.fmi_no_gps_data),
Toast.LENGTH_SHORT).show();
}
// *********************************************************************************
// * Transitions
// *********************************************************************************
}